package formatStruct

import (
	"duanju-mq/pkg/util/convert"
	"errors"
	"fmt"
	"net/url"
	"reflect"
	"strconv"
	"strings"
)

type ParseStructData struct {
	cols        []string
	vals        []any
	ptrs        []any
	filedsValue map[string]any
	iextStr     []string
	flashFlag   bool
	orgData     any
}

// 传结构体， 屏蔽解析某些字段....
func ParseStructFields(data any, iexStr ...string) *ParseStructData {
	structData := &ParseStructData{cols: []string{}, iextStr: iexStr, flashFlag: true, orgData: data, filedsValue: make(map[string]any)}
	structData.flashFields()
	return structData
}
func (structData *ParseStructData) flashFields() {
	if !structData.flashFlag {
		return
	}
	structData.cols = []string{}
	structData.vals = []any{}
	structData.flashFlag = false
	t := reflect.TypeOf(structData.orgData).Elem()
	v := reflect.ValueOf(structData.orgData).Elem()
	for k := 0; k < t.NumField(); k++ {
		f := t.Field(k)
		if !firstIsUpper(f.Name) {
			continue
		}
		if f.Anonymous {
			cData := ParseStructFields(v.Field(k).Addr().Interface(), structData.iextStr...)
			structData.cols = append(structData.cols, cData.cols...)
			structData.vals = append(structData.vals, cData.vals...)
			structData.ptrs = append(structData.ptrs, cData.ptrs...)
			continue
		}
		field := f.Name
		if tag := f.Tag.Get("field"); tag != "" {
			if tag == "-" {
				continue
			}
			field = tag
		}
		if len(structData.iextStr) > 0 && InStringArray(field, structData.iextStr) {
			continue
		}
		structData.cols = append(structData.cols, field)
		structData.vals = append(structData.vals, v.Field(k).Interface())
		structData.ptrs = append(structData.ptrs, v.Field(k).Addr().Interface())
	}
	for k, v := range structData.cols {
		structData.filedsValue[v] = structData.vals[k]
	}
}

// 字段默认用逗号拼接
func (this *ParseStructData) ColsToString(s ...string) string {
	sep := ","
	if len(s) >= 1 {
		sep = s[0]
	}
	return strings.Join(this.cols, sep)
}

// 解析成 ?,?,?... 格式
func (this *ParseStructData) ColsReplaceStr() string {
	s := []string{}
	for _, _ = range this.cols {
		s = append(s, "?")
	}
	return strings.Join(s, ",")
}

// 解析成 field=?,field2=?,field3=? 格式
func (this *ParseStructData) ColsUpdateSqlStr() string {
	s := []string{}
	for _, filed := range this.cols {
		s = append(s, filed+"=?")
	}
	return strings.Join(s, ",")
}

// 解析url参数并且复制给vals
func (this *ParseStructData) Unmarshal(args url.Values) *ParseStructData {
	for index, filed := range this.cols {
		dest := this.ptrs[index]
		switch dest.(type) {
		case *string:
			this.vals[index] = args.Get(filed)
		case *int:
			this.vals[index] = convert.Atoi(args.Get(filed))
		case *int64:
			this.vals[index] = int64(convert.Atoi(args.Get(filed)))
		case *float32:
			this.vals[index], _ = strconv.ParseFloat(args.Get(filed), 32)
		case *float64:
			this.vals[index], _ = strconv.ParseFloat(args.Get(filed), 64)
		}
	}
	return this
}

func (this *ParseStructData) GetPtrs() []interface{} {
	this.flashFlag = true
	return this.ptrs
}

func (this *ParseStructData) GetCols() []string {
	return this.cols
}

// return sql语句，sql传参变量
func (this *ParseStructData) FormatAddSql(tableName string, args url.Values) (string, []interface{}) {
	this.Unmarshal(args)
	return this.FormatAddSqlNoArgs(tableName)
}

// return sql语句，直接格式自身字段变量
func (this *ParseStructData) FormatAddSqlNoArgs(tableName string) (string, []interface{}) {
	this.flashFields()
	return "INSERT INTO " + tableName + "(`" + this.ColsToString("`,`") + "`) VALUES(" + this.ColsReplaceStr() + ")", this.vals
}

// return sql语句，sql传参变量
func (this *ParseStructData) FormatUpdateSql(tableName string, args url.Values, wherefields ...string) (string, []any) {
	sql := "UPDATE " + tableName + " set " + this.ColsUpdateSqlStr()
	values := this.Unmarshal(args).vals
	if len(wherefields) > 0 {
		sql += " WHERE "
		for i, filed := range wherefields {
			if i > 0 {
				sql += " AND "
			}
			sql += filed + "=?"
			values = append(values, args.Get(filed))
		}
	}
	return sql, values
}

// return sql语句，直接格式自身字段变量
func (this *ParseStructData) FormatUpdateSqlNoArgs(tableName string, wherefields ...string) (string, []interface{}) {
	this.flashFields()
	sql := "UPDATE " + tableName + " set " + this.ColsUpdateSqlStr()
	values := this.vals
	if len(wherefields) > 0 {
		sql += " WHERE "
		for i, filed := range wherefields {
			if i > 0 {
				sql += " AND "
			}
			sql += filed + "=?"
			values = append(values, this.GetField(filed))
		}
	}
	return sql, values
}
func (this *ParseStructData) GetField(field string) any {
	if v, ok := this.filedsValue[field]; ok {
		return v
	}
	return ""
}
func InStringArray(a string, arr []string) bool {
	arrStr := "," + strings.Join(arr, ",") + ","
	skey := "," + a + ","
	return strings.Contains(arrStr, skey)
}

func firstIsUpper(str string) bool {
	if len(str) < 1 {
		return false
	}
	strArry := []rune(str)
	if strArry[0] >= 65 && strArry[0] <= 90 {
		return true
	}
	return false
}

func ParseUrlSelectData(urlFileds []string, tableFileds []string, args url.Values) ([]string, []interface{}) {
	var (
		filedKeys  []string
		filedValue []interface{}
	)
	for index, filed := range urlFileds {
		value := args.Get(filed)
		if value != "" && value != "-1" {
			filedKeys = append(filedKeys, tableFileds[index])
			filedValue = append(filedValue, value)
		}
	}
	return filedKeys, filedValue
}

var (
	maxBatchSize = 3000
)

func SetMaxBatchSize(size int) {
	maxBatchSize = size
}
func BatchFormatAddSql[T any](datas []T, tableName string, iexStr ...string) (string, []interface{}, error) {
	if len(datas) <= 0 {
		return "", nil, errors.New("DatasWasEmpty")
	}
	if len(datas) > maxBatchSize {
		return "", nil, errors.New("OverMaxSize")
	}
	fpt := ParseStructFields(datas[0], iexStr...)
	sql := fmt.Sprintf("INSERT INTO %s(%s)VALUES", tableName, fmt.Sprintf("`%s`", fpt.ColsToString("`,`")))
	batchVals := []any{}
	buf := strings.Builder{}
	for i, data := range datas {
		fpt = ParseStructFields(data, iexStr...)
		if i > 0 {
			buf.WriteString(",")
		}
		buf.WriteString("(" + fpt.ColsReplaceStr() + ")")
		batchVals = append(batchVals, fpt.vals...)
	}
	sql += buf.String()
	return sql, batchVals, nil
}
